#property copyright "Cheng Yan"
#property link      "6266616"
#property version   "1.10"
#property strict

#include "../../Include/Trade/Trade.mqh"
#include "../../Include/Trade/PositionInfo.mqh"

//--- 交易模式枚举
enum EnumTradeMode
  {
   TRADE_ON_TICK = 0, // 即时交易
   TRADE_ON_BAR = 1   // 收盘交易
  };

//--- EA 输入参数
// sgroup是mql5中用于分组显示的关键字
input string             ma_params_group = "--------- MA Parameters ---------";
input int                ma_period_a = 5;
input int                ma_period_b = 8;
input int                ma_period_c = 13;
input int                ma_period_d = 21;
input int                ma_period_e = 34;
input int                ma_period_f = 55;
input ENUM_MA_METHOD     ma_method = MODE_EMA;
input ENUM_APPLIED_PRICE ma_applied_price = PRICE_CLOSE;

input string             ma_filter_group = "--------- MA Filter Parameters ---------";
input bool               enable_ma_filter = false;
input int                filter_ma_period_a = 1;
input int                filter_ma_period_b = 1;
input int                filter_ma_period_c = 1;
input int                filter_ma_period_d = 46;
input int                filter_ma_period_e = 46;
input int                filter_ma_period_f = 46;
input ENUM_MA_METHOD     filter_ma_method = MODE_SMA;
input ENUM_APPLIED_PRICE filter_ma_applied_price = PRICE_CLOSE;

input string             trade_settings_group = "--------- Trade Settings ---------";
input EnumTradeMode      trade_mode = TRADE_ON_BAR;
input int                add_pos_with_trend_pips = 1000;  // 间隔N微点顺势加仓_0为关闭
input int                add_pos_against_trend_pips = 2000; // 间隔N微点逆势加仓_0为关闭

input string             lot_management_group = "--------- Lot Management ---------";
input double             initial_lot = 0.01;
input bool               enable_auto_lot = true;
input int                dollars_per_001_lot = 1000; // 每N美元余额做0.01手
input double             with_trend_lot_multiplier = 1.0;
input double             against_trend_lot_multiplier = 1.0;
input int                max_with_trend_adds = 2;
input int                max_against_trend_adds = 3;

input string             sl_tp_group = "--------- SL/TP Settings ---------";
input int                avg_price_take_profit_pips = 4000;   // 单向均价止盈N微点_0为关闭
input int                avg_price_stop_loss_pips = 0;     // 关闭基于均价的止损
input int                wait_seconds_after_sl = 0;        // 直接止损后N秒内禁止建仓
input int                trailing_start_pips = 1500;          // 单向均价盈利N微点后_0为关闭
input int                trailing_stop_pips = 1000;           // 追踪止损于盈利回撤N微点平仓_0为关闭
input int                wait_seconds_after_trailing_sl = 0; // 移动止损后N秒内禁止建仓

input string             close_all_group = "--------- Close All Conditions ---------";
input double             close_all_profit_dollars = 0.0;    // 总盈利大于N美元全平_0为关闭
input double             close_all_loss_dollars = 200.0;    // 当总浮亏达到200美元时，平掉所有仓位
input int                close_all_profit_per_lot = 0;      // 总盈利大于总手数乘N全平_0为关闭
input int                close_all_loss_per_lot = 0;        // 总亏损大于总手数乘N全平_0为关闭
input double             close_all_loss_balance_pct = 0.0;  // 或者您也可以使用这个：close_all_loss_balance_pct = 20.0;
input bool               enable_ma_cross_close_all = false;
input int                close_ma_period_a = 5;
input int                close_ma_period_b = 46;
input ENUM_MA_METHOD     close_ma_method = MODE_SMA;
input ENUM_APPLIED_PRICE close_ma_applied_price = PRICE_CLOSE;

input string             display_group = "--------- Display Settings ---------";
input bool               show_info_panel = true;
input color              info_panel_color = clrDeepSkyBlue;
input int                info_panel_x_offset = 300;
input int                magic_number = 6266616;

// EA主类
class CTrendEA
  {
private:
   // 交易和持仓对象
   CTrade            m_trade;
   CPositionInfo     m_pos_info;

   // 成员变量
   string            m_symbol;
   ENUM_TIMEFRAMES   m_period;
   int               m_digits;
   double            m_point;
   double            m_tick_size;
   double            m_min_lot;
   double            m_max_lot;
   double            m_lot_step;
   double            m_tick_value;
   int               m_stops_level;

   double            m_account_balance;
   double            m_account_equity;
   long              m_account_number;
   
   // 历史交易统计变量
   int               m_h_sl_count;
   datetime          m_h_last_close_time;
   double            m_h_last_profit;

   // 统计变量
   int               m_total_positions;
   int               m_buy_positions_count;
   int               m_sell_positions_count;
   double            m_buy_lots;
   double            m_sell_lots;
   double            m_total_lots;
   double            m_buy_profit;
   double            m_sell_profit;
   double            m_total_profit;
   
   double            m_avg_buy_price;
   double            m_avg_sell_price;
   
   double            m_highest_buy_price;
   double            m_lowest_buy_price;
   double            m_highest_sell_price;
   double            m_lowest_sell_price;

   double            m_highest_buy_lots;
   double            m_lowest_buy_lots;
   double            m_highest_sell_lots;
   double            m_lowest_sell_lots;

   datetime          m_last_trade_time;


public:
   // 构造函数
                     CTrendEA(void);
                    ~CTrendEA(void);

   // 主事件处理
   void              OnTick(void);
   void              OnInit(void);
   void              OnDeinit(void);

private:
   // 私有方法
   void              UpdateMarketData(void);
   void              AnalyzeTradeHistory(void);
   void              CalculatePositionStats(void);
   void              UpdateDisplay(void);
   void              CheckGlobalCloseConditions(void);
   void              ManageOpenPositions(void);
   void              CheckNewTradeSignals(void);
   bool              IsTradingAllowedByHistory(void);
   void              ExecuteTrade(ENUM_ORDER_TYPE type, double lots);
   double            CalculateLotSize(ENUM_ORDER_TYPE direction);
   void              CreateLabel(string name, int x, int y, string text, int size, color clr);
   void              ErrorAlert(string message, int error_code=0);
  };

CTrendEA ExtExpert;

//+------------------------------------------------------------------+
int OnInit()
  {
   ExtExpert.OnInit();
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ExtExpert.OnDeinit();
  }
//+------------------------------------------------------------------+
void OnTick()
  {
   ExtExpert.OnTick();
  }
//+------------------------------------------------------------------+
void OnTimer()
  {
   ExtExpert.OnTick();
  }

//+------------------------------------------------------------------+
//| CTrendEA Class Implementation                                    |
//+------------------------------------------------------------------+
CTrendEA::CTrendEA(void)
  {
  }
//+------------------------------------------------------------------+
CTrendEA::~CTrendEA(void)
  {
  }
//+------------------------------------------------------------------+
void CTrendEA::OnInit(void)
  {
   Print("EA Initializing...");
   m_symbol = Symbol();
   m_period = Period();
   EventSetMillisecondTimer(500); // 设置一个更合理的计时器
  }
//+------------------------------------------------------------------+
void CTrendEA::OnDeinit(void)
  {
   Print("EA Deinitializing...");
   EventKillTimer();
   ObjectsDeleteAll(0, "_YCEA");
  }
//+------------------------------------------------------------------+
void CTrendEA::OnTick(void)
  {
   // 1. 更新数据
   UpdateMarketData();

   // 2. 分析交易历史
   AnalyzeTradeHistory();
   
   // 3. 计算持仓统计
   CalculatePositionStats();

   // 4. 更新图表显示
   UpdateDisplay();

   // 5. 检查全局平仓条件
   CheckGlobalCloseConditions();
   
   // 6. 管理现有持仓 (止损、止盈、移动止损)
   ManageOpenPositions();

   // 7. 检查并执行新交易
   CheckNewTradeSignals();
  }

// --- 以下是待实现的私有方法 ---

void CTrendEA::UpdateMarketData(void)
  {
   // 实现市场和账户数据的更新
   m_symbol = Symbol();
   m_period = Period();
   m_digits = (int)SymbolInfoInteger(m_symbol, SYMBOL_DIGITS);
   m_point = SymbolInfoDouble(m_symbol, SYMBOL_POINT);
   m_tick_size = SymbolInfoDouble(m_symbol, SYMBOL_TRADE_TICK_SIZE);
   m_min_lot = SymbolInfoDouble(m_symbol, SYMBOL_VOLUME_MIN);
   m_max_lot = SymbolInfoDouble(m_symbol, SYMBOL_VOLUME_MAX);
   m_lot_step = SymbolInfoDouble(m_symbol, SYMBOL_VOLUME_STEP);
   m_tick_value = SymbolInfoDouble(m_symbol, SYMBOL_TRADE_TICK_VALUE);
   m_stops_level = (int)SymbolInfoInteger(m_symbol, SYMBOL_TRADE_STOPS_LEVEL);
   
   m_account_balance = AccountInfoDouble(ACCOUNT_BALANCE);
   m_account_equity = AccountInfoDouble(ACCOUNT_EQUITY);
   m_account_number = AccountInfoInteger(ACCOUNT_LOGIN);
  }

void CTrendEA::AnalyzeTradeHistory(void)
{
    // 重置统计变量
    m_h_sl_count = 0;
    m_h_last_close_time = 0;
    m_h_last_profit = 0;
    
    if(!HistorySelect(0, TimeCurrent())) return;
    
    int total = HistoryOrdersTotal();
    for(int i = total - 1; i >= 0; i--)
    {
        ulong ticket = HistoryOrderGetTicket(i);
        if (ticket > 0)
        {
            if (HistoryOrderSelect(ticket))
            {
                if (HistoryOrderGetString(ticket, ORDER_SYMBOL) == m_symbol && HistoryOrderGetInteger(ticket, ORDER_MAGIC) == magic_number)
                {
                     if(HistoryOrderGetInteger(ticket, ORDER_STATE) == ORDER_STATE_FILLED || HistoryOrderGetInteger(ticket, ORDER_STATE) == ORDER_STATE_PARTIAL)
                     {
                         // 检查是否是止损
                         double sl = HistoryOrderGetDouble(ticket, ORDER_SL);
                         if (sl > 0)
                         {
                             ulong deal_ticket = HistoryDealGetTicket(ticket);
                             double close_price = HistoryDealGetDouble(deal_ticket, DEAL_PRICE);
                             ENUM_ORDER_TYPE type = (ENUM_ORDER_TYPE)HistoryOrderGetInteger(ticket, ORDER_TYPE);
                             if ((type == ORDER_TYPE_BUY && close_price <= sl) || (type == ORDER_TYPE_SELL && close_price >= sl))
                             {
                                 m_h_sl_count++;
                             }
                         }
                         // 获取最后一个平仓订单的时间和盈亏
                         if(HistoryOrderGetInteger(ticket, ORDER_TIME_DONE) > m_h_last_close_time)
                         {
                             m_h_last_close_time = HistoryOrderGetInteger(ticket, ORDER_TIME_DONE);
                             ulong deal_ticket = HistoryDealGetTicket(ticket);
                             m_h_last_profit = HistoryDealGetDouble(deal_ticket, DEAL_PROFIT) + HistoryDealGetDouble(deal_ticket, DEAL_SWAP) + HistoryDealGetDouble(deal_ticket, DEAL_COMMISSION);
                         }
                     }
                }
            }
        }
    }
}

void CTrendEA::CalculatePositionStats(void)
  {
   // 重置统计变量
   m_total_positions = 0;
   m_buy_positions_count = 0;
   m_sell_positions_count = 0;
   m_buy_lots = 0;
   m_sell_lots = 0;
   m_total_lots = 0;
   m_buy_profit = 0;
   m_sell_profit = 0;
   m_total_profit = 0;
   m_avg_buy_price = 0;
   m_avg_sell_price = 0;
   m_highest_buy_price = 0;
   m_lowest_buy_price = 0;
   m_highest_sell_price = 0;
   m_lowest_sell_price = 0;
   
   double buy_weighted_price_sum = 0;
   double sell_weighted_price_sum = 0;

   m_total_positions = PositionsTotal();
   for(int i = m_total_positions - 1; i >= 0; i--)
     {
      if(m_pos_info.SelectByIndex(i))
        {
         if(m_pos_info.Symbol() == m_symbol && m_pos_info.Magic() == magic_number)
           {
            m_total_profit += m_pos_info.Profit() + m_pos_info.Swap() + m_pos_info.Commission();

            if(m_pos_info.PositionType() == POSITION_TYPE_BUY)
              {
               m_buy_positions_count++;
               m_buy_lots += m_pos_info.Volume();
               m_buy_profit += m_pos_info.Profit() + m_pos_info.Swap() + m_pos_info.Commission();
               buy_weighted_price_sum += m_pos_info.PriceOpen() * m_pos_info.Volume();

               if(m_highest_buy_price == 0 || m_pos_info.PriceOpen() > m_highest_buy_price) {
                  m_highest_buy_price = m_pos_info.PriceOpen();
                  m_highest_buy_lots = m_pos_info.Volume();
               }
               if(m_lowest_buy_price == 0 || m_pos_info.PriceOpen() < m_lowest_buy_price) {
                  m_lowest_buy_price = m_pos_info.PriceOpen();
                  m_lowest_buy_lots = m_pos_info.Volume();
               }

              }
            else if(m_pos_info.PositionType() == POSITION_TYPE_SELL)
              {
               m_sell_positions_count++;
               m_sell_lots += m_pos_info.Volume();
               m_sell_profit += m_pos_info.Profit() + m_pos_info.Swap() + m_pos_info.Commission();
               sell_weighted_price_sum += m_pos_info.PriceOpen() * m_pos_info.Volume();

               if(m_highest_sell_price == 0 || m_pos_info.PriceOpen() > m_highest_sell_price) {
                  m_highest_sell_price = m_pos_info.PriceOpen();
                  m_highest_sell_lots = m_pos_info.Volume();
               }
               if(m_lowest_sell_price == 0 || m_pos_info.PriceOpen() < m_lowest_sell_price) {
                  m_lowest_sell_price = m_pos_info.PriceOpen();
                  m_lowest_sell_lots = m_pos_info.Volume();
               }
              }
           }
        }
     }
     
   m_total_lots = m_buy_lots + m_sell_lots;
   if(m_buy_lots > 0)
      m_avg_buy_price = buy_weighted_price_sum / m_buy_lots;
   if(m_sell_lots > 0)
      m_avg_sell_price = sell_weighted_price_sum / m_sell_lots;

  }

void CTrendEA::UpdateDisplay(void)
  {
   if(!show_info_panel)
     {
      ObjectsDeleteAll(0, "Name_YCEA");
      Comment("");
      return;
     }

   string content[9];
   int i = 0;
   content[i++] = "【Trend EA Status】";
   content[i++] = "Min Lot: " + DoubleToString(m_min_lot, 2) + ", Stops Level: " + (string)m_stops_level + ", Spread: " + (string)SymbolInfoInteger(m_symbol, SYMBOL_SPREAD);
   content[i++] = "Server Time: " + TimeToString(TimeCurrent()) + ", Local Time: " + TimeToString(TimeLocal(), TIME_SECONDS);
   content[i++] = "---------";
   content[i++] = "Buy Positions: " + (string)m_buy_positions_count + ", Lots: " + DoubleToString(m_buy_lots, 2) + ", Profit: " + DoubleToString(m_buy_profit, 2);
   content[i++] = "Sell Positions: " + (string)m_sell_positions_count + ", Lots: " + DoubleToString(m_sell_lots, 2) + ", Profit: " + DoubleToString(m_sell_profit, 2);
   content[i++] = "---------";
   content[i++] = "Account Balance: " + DoubleToString(m_account_balance, 2) + ", Equity: " + DoubleToString(m_account_equity, 2);
   content[i++] = "Total Profit: " + DoubleToString(m_total_profit, 2) + ", Magic: " + (string)magic_number;

   for(int j=0; j<i; j++)
   {
       CreateLabel("Name_YCEA"+(string)j, info_panel_x_offset, 20 + j*18, content[j], 10, info_panel_color);
   }
   Comment(""); // 清除角落的注释
  }

void CTrendEA::CheckGlobalCloseConditions(void)
{
    bool close_all = false;
    if(close_all_profit_dollars > 0 && m_total_profit >= close_all_profit_dollars) close_all = true;
    if(close_all_loss_dollars > 0 && m_total_profit <= -close_all_loss_dollars) close_all = true;
    if(close_all_profit_per_lot > 0 && m_total_profit >= close_all_profit_per_lot * m_total_lots * m_tick_value) close_all = true;
    if(close_all_loss_per_lot > 0 && m_total_profit <= -close_all_loss_per_lot * m_total_lots * m_tick_value) close_all = true;
    if(close_all_loss_balance_pct > 0 && m_total_profit <= -(close_all_loss_balance_pct / 100.0) * m_account_balance) close_all = true;

    if(enable_ma_cross_close_all)
    {
        double close_ma_a[2], close_ma_b[2];
        CopyBuffer(iMA(m_symbol, m_period, close_ma_period_a, 0, close_ma_method, close_ma_applied_price), 0, 0, 2, close_ma_a);
        CopyBuffer(iMA(m_symbol, m_period, close_ma_period_b, 0, close_ma_method, close_ma_applied_price), 0, 0, 2, close_ma_b);

        if((close_ma_a[1] <= close_ma_b[1] && close_ma_a[0] > close_ma_b[0]) || (close_ma_a[1] >= close_ma_b[1] && close_ma_a[0] < close_ma_b[0]))
        {
            close_all = true;
        }
    }

    if(close_all)
    {
        for(int i = PositionsTotal() - 1; i >= 0; i--)
        {
            if(m_pos_info.SelectByIndex(i) && m_pos_info.Magic() == magic_number && m_pos_info.Symbol() == m_symbol)
            {
                m_trade.PositionClose(m_pos_info.Ticket());
            }
        }
    }
}

void CTrendEA::ManageOpenPositions(void)
{
    MqlTick last_tick;
    SymbolInfoTick(m_symbol, last_tick);

    for(int i = PositionsTotal() - 1; i >= 0; i--)
    {
        if(m_pos_info.SelectByIndex(i) && m_pos_info.Magic() == magic_number && m_pos_info.Symbol() == m_symbol)
        {
            double sl = m_pos_info.StopLoss();
            double tp = m_pos_info.TakeProfit();
            double new_sl = sl;
            double new_tp = tp;

            // 均价止损止盈
            if(m_pos_info.PositionType() == POSITION_TYPE_BUY)
            {
                if(avg_price_stop_loss_pips > 0) new_sl = m_avg_buy_price - avg_price_stop_loss_pips * m_point;
                if(avg_price_take_profit_pips > 0) new_tp = m_avg_buy_price + avg_price_take_profit_pips * m_point;
            }
            else
            {
                if(avg_price_stop_loss_pips > 0) new_sl = m_avg_sell_price + avg_price_stop_loss_pips * m_point;
                if(avg_price_take_profit_pips > 0) new_tp = m_avg_sell_price - avg_price_take_profit_pips * m_point;
            }

            // 移动止损
            if(trailing_start_pips > 0 && trailing_stop_pips > 0)
            {
                if(m_pos_info.PositionType() == POSITION_TYPE_BUY)
                {
                    if(last_tick.bid - m_pos_info.PriceOpen() > trailing_start_pips * m_point)
                    {
                        double trailing_sl = last_tick.bid - trailing_stop_pips * m_point;
                        if(new_sl < trailing_sl) new_sl = trailing_sl;
                    }
                }
                else
                {
                    if(m_pos_info.PriceOpen() - last_tick.ask > trailing_start_pips * m_point)
                    {
                        double trailing_sl = last_tick.ask + trailing_stop_pips * m_point;
                        if(new_sl > trailing_sl || new_sl == 0) new_sl = trailing_sl;
                    }
                }
            }
            
            // 修改订单
            if(MathAbs(sl - new_sl) > m_tick_size || MathAbs(tp - new_tp) > m_tick_size)
            {
                m_trade.PositionModify(m_pos_info.Ticket(), NormalizeDouble(new_sl, m_digits), NormalizeDouble(new_tp, m_digits));
            }
        }
    }
}


void CTrendEA::CheckNewTradeSignals(void)
{
    MqlTick last_tick;
    SymbolInfoTick(m_symbol, last_tick);

    // MA 信号
    double ma_a[2], ma_b[2], ma_c[2], ma_d[2], ma_e[2], ma_f[2];
    CopyBuffer(iMA(m_symbol, m_period, ma_period_a, 0, ma_method, ma_applied_price), 0, 0, 2, ma_a);
    CopyBuffer(iMA(m_symbol, m_period, ma_period_b, 0, ma_method, ma_applied_price), 0, 0, 2, ma_b);
    CopyBuffer(iMA(m_symbol, m_period, ma_period_c, 0, ma_method, ma_applied_price), 0, 0, 2, ma_c);
    CopyBuffer(iMA(m_symbol, m_period, ma_period_d, 0, ma_method, ma_applied_price), 0, 0, 2, ma_d);
    CopyBuffer(iMA(m_symbol, m_period, ma_period_e, 0, ma_method, ma_applied_price), 0, 0, 2, ma_e);
    CopyBuffer(iMA(m_symbol, m_period, ma_period_f, 0, ma_method, ma_applied_price), 0, 0, 2, ma_f);

    // With default (non-series) array, index 0 is previous bar [shift 1], index 1 is current bar [shift 0].
    // A buy signal is when the MA is rising (current > previous).
    bool is_buy_signal = ma_a[1] > ma_a[0] &&
                         ma_b[1] > ma_b[0] &&
                         ma_c[1] > ma_c[0] &&
                         ma_d[1] > ma_d[0] &&
                         ma_e[1] > ma_e[0] &&
                         ma_f[1] > ma_f[0];

    bool is_sell_signal = ma_a[1] < ma_a[0] &&
                          ma_b[1] < ma_b[0] &&
                          ma_c[1] < ma_c[0] &&
                          ma_d[1] < ma_d[0] &&
                          ma_e[1] < ma_e[0] &&
                          ma_f[1] < ma_f[0];

    // MA 过滤
    if(enable_ma_filter)
    {
        double f_ma_a[1], f_ma_b[1], f_ma_c[1], f_ma_d[1], f_ma_e[1], f_ma_f[1];
        CopyBuffer(iMA(m_symbol, m_period, filter_ma_period_a, 0, filter_ma_method, filter_ma_applied_price), 0, 0, 1, f_ma_a);
        CopyBuffer(iMA(m_symbol, m_period, filter_ma_period_b, 0, filter_ma_method, filter_ma_applied_price), 0, 0, 1, f_ma_b);
        CopyBuffer(iMA(m_symbol, m_period, filter_ma_period_c, 0, filter_ma_method, filter_ma_applied_price), 0, 0, 1, f_ma_c);
        CopyBuffer(iMA(m_symbol, m_period, filter_ma_period_d, 0, filter_ma_method, filter_ma_applied_price), 0, 0, 1, f_ma_d);
        CopyBuffer(iMA(m_symbol, m_period, filter_ma_period_e, 0, filter_ma_method, filter_ma_applied_price), 0, 0, 1, f_ma_e);
        CopyBuffer(iMA(m_symbol, m_period, filter_ma_period_f, 0, filter_ma_method, filter_ma_applied_price), 0, 0, 1, f_ma_f);
        
        if(is_buy_signal && !(f_ma_a[0] >= f_ma_b[0] && f_ma_b[0] >= f_ma_c[0] && f_ma_c[0] >= f_ma_d[0] && f_ma_d[0] >= f_ma_e[0] && f_ma_e[0] >= f_ma_f[0]))
        {
            is_buy_signal = false;
        }
        if(is_sell_signal && !(f_ma_a[0] <= f_ma_b[0] && f_ma_b[0] <= f_ma_c[0] && f_ma_c[0] <= f_ma_d[0] && f_ma_d[0] <= f_ma_e[0] && f_ma_e[0] <= f_ma_f[0]))
        {
            is_sell_signal = false;
        }
    }
    
    // 开仓逻辑
    bool can_trade = IsTradingAllowedByHistory(); 
    
    if(m_total_positions == 0 && can_trade)
    {
        if(is_buy_signal) ExecuteTrade(ORDER_TYPE_BUY, CalculateLotSize(ORDER_TYPE_BUY));
        if(is_sell_signal) ExecuteTrade(ORDER_TYPE_SELL, CalculateLotSize(ORDER_TYPE_SELL));
    }
    else 
    {
        int with_trend_buy_count = 0;
        int against_trend_buy_count = 0;
        int with_trend_sell_count = 0;
        int against_trend_sell_count = 0;

        for(int i = 0; i < PositionsTotal(); i++) {
             if (m_pos_info.SelectByIndex(i)) {
                 if (m_pos_info.PositionType() == POSITION_TYPE_BUY) {
                     if (m_pos_info.PriceOpen() > m_avg_buy_price) {
                         with_trend_buy_count++;
                     } else {
                         against_trend_buy_count++;
                     }
                 } else {
                     if (m_pos_info.PriceOpen() < m_avg_sell_price) {
                         with_trend_sell_count++;
                     } else {
                         against_trend_sell_count++;
                     }
                 }
             }
         }
        
        if(m_buy_positions_count > 0 && add_pos_with_trend_pips > 0 && last_tick.ask - m_highest_buy_price >= add_pos_with_trend_pips * m_point && with_trend_buy_count < max_with_trend_adds)
        {
            ExecuteTrade(ORDER_TYPE_BUY, CalculateLotSize(ORDER_TYPE_BUY));
        }
        if(m_sell_positions_count > 0 && add_pos_with_trend_pips > 0 && m_lowest_sell_price - last_tick.bid >= add_pos_with_trend_pips * m_point && with_trend_sell_count < max_with_trend_adds)
        {
            ExecuteTrade(ORDER_TYPE_SELL, CalculateLotSize(ORDER_TYPE_SELL));
        }
        if(m_buy_positions_count > 0 && add_pos_against_trend_pips > 0 && m_lowest_buy_price - last_tick.ask >= add_pos_against_trend_pips * m_point && against_trend_buy_count < max_against_trend_adds)
        {
            ExecuteTrade(ORDER_TYPE_BUY, CalculateLotSize(ORDER_TYPE_BUY));
        }
        if(m_sell_positions_count > 0 && add_pos_against_trend_pips > 0 && last_tick.bid - m_highest_sell_price >= add_pos_against_trend_pips * m_point && against_trend_sell_count < max_against_trend_adds)
        {
            ExecuteTrade(ORDER_TYPE_SELL, CalculateLotSize(ORDER_TYPE_SELL));
        }
    }
}

bool CTrendEA::IsTradingAllowedByHistory(void)
{
    if (m_total_positions > 0) return true;

    if (m_h_sl_count > 0)
    {
        if (m_h_last_profit < 0 && wait_seconds_after_sl > 0 && (TimeCurrent() - m_h_last_close_time) <= wait_seconds_after_sl)
        {
            return false;
        }
        if (m_h_last_profit >= 0 && wait_seconds_after_trailing_sl > 0 && (TimeCurrent() - m_h_last_close_time) <= wait_seconds_after_trailing_sl)
        {
            return false;
        }
    }
    return true;
}


void CTrendEA::ExecuteTrade(ENUM_ORDER_TYPE type, double lots)
  {
   if(lots < m_min_lot) return;
   
   MqlTradeRequest request;
   MqlTradeResult result;
   ZeroMemory(request);
   ZeroMemory(result);
   
   double price = (type == ORDER_TYPE_BUY) ? SymbolInfoDouble(m_symbol, SYMBOL_ASK) : SymbolInfoDouble(m_symbol, SYMBOL_BID);
   
   request.action = TRADE_ACTION_DEAL;
   request.symbol = m_symbol;
   request.volume = lots;
   request.type = type;
   request.price = price;
   request.deviation = 5;
   request.magic = magic_number;
   request.comment = "Trend EA";
   
   if(!m_trade.OrderSend(request, result))
     {
      ErrorAlert("OrderSend failed", result.retcode);
     }
  }

double CTrendEA::CalculateLotSize(ENUM_ORDER_TYPE direction)
  {
   MqlTick last_tick;
   SymbolInfoTick(m_symbol, last_tick);

   double lots = initial_lot;
   if(enable_auto_lot)
     {
      lots = NormalizeDouble(m_account_balance / dollars_per_001_lot * 0.01, 2);
     }

   if(m_total_positions > 0)
   {
       if(direction == ORDER_TYPE_BUY)
       {
           if(last_tick.ask > m_highest_buy_price) // With trend
           {
               lots = m_highest_buy_lots * with_trend_lot_multiplier;
           }
           else // Against trend
           {
               lots = m_lowest_buy_lots * against_trend_lot_multiplier;
           }
       }
       else // ORDER_TYPE_SELL
       {
           if(last_tick.bid < m_lowest_sell_price) // With trend
           {
               lots = m_lowest_sell_lots * with_trend_lot_multiplier;
           }
           else // Against trend
           {
               lots = m_highest_sell_lots * against_trend_lot_multiplier;
           }
       }
   }

   // 规整手数
   lots = NormalizeDouble(round(lots / m_lot_step) * m_lot_step, 2);
   if(lots < m_min_lot) lots = m_min_lot;
   if(lots > m_max_lot) lots = m_max_lot;
   
   return lots;
  }

void CTrendEA::CreateLabel(string name, int x, int y, string text, int size, color clr)
  {
    if(ObjectFind(0, name) == -1)
    {
        ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
        ObjectSetString(0, name, OBJPROP_FONT, "Arial");
        ObjectSetInteger(0, name, OBJPROP_FONTSIZE, size);
        ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
        ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
        ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER);
    }
    ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
    ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
    ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
    ObjectSetString(0, name, OBJPROP_TEXT, text);
  }

void CTrendEA::ErrorAlert(string message, int error_code=0)
{
    if (MQLInfoInteger(MQL_OPTIMIZATION)) return;

    int error = (error_code == 0) ? GetLastError() : error_code;
    if (error == 0) return;

    string text = message;
    
    if(error == 10004) text+=": Requote";
    else if(error == 10006) text+=": Request rejected";
    else if(error == 10007) text+=": Request canceled by trader";
    else if(error == 10008) text+=": Order placed";
    else if(error == 10009) text+=": Request completed";
    else if(error == 10010) text+=": Only part of the request was completed";
    else if(error == 10013) text+=": Invalid request";
    else if(error == 10014) text+=": Invalid volume in the request";
    else if(error == 10015) text+=": Invalid price in the request";
    else if(error == 10016) text+=": Invalid stops in the request";
    else if(error == 10017) text+=": Trade is disabled";
    else if(error == 10018) text+=": Market is closed";
    else if(error == 10019) text+=": There is not enough money to complete the request";
    else if(error == 10020) text+=": Prices changed";
    else if(error == 10021) text+=": There are no quotes to process the request";
    else if(error == 10024) text+=": Request canceled by timeout";
    else if(error == 10025) text+=": Invalid order filling type";
    else if(error == 10030) text+=": No connection with the trade server";
    else text += ": Unknown trade server return code";

    Print(message, " (Code: ", error, ", Description: ", text, ")");
    Alert(message + " (Code: " + (string)error + ")");
} 